#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "constantes_et_macros.h"

#include "carte_zone.h"
#include "parsing.h"
#include "cases.h"
#include "manipulation_cases.h"
#include "manipulation_ennemis.h"
#include "etat_partie.h"
#include "ennemis.h"
#include "def_etat_exploration.h"




/**
 * \fn int get_prochaine_ligne(FILE* fichier, char buffer[4096])
 * \brief Met dans le buffer la premiere ligne trouvee qui n'est
 * ni un commentaire ni une ligne vide
 *
 * \param[in] fichier Le fichier dans lequel on lit
 * \param[out] buffer Un pointeur vers un buffer de 4096 caracteres
 * \return -1 si EOF avant d'avoir trouve une ligne utile, 1 si tout se passe bien
 *
 * parcours un fichier jusqu'a trouver une ligne n'etant
 * ni un commentaire, ni une ligne vide (les lignes de
 * commentaires sont celles commencant par #)
 */
static int get_prochaine_ligne(FILE* fichier, char buffer[4096])
{
    int longueur;

    do
    {
        if(!fgets(buffer, 4096, fichier))
            return -1;
    } while(buffer[0] == '#' || buffer[0] == '\n' || buffer[0] == '\r');
    /*
     * le '\n' recupere par fgets est rarement utile
     * et souvent genant, cette fonction le retire donc
     */
    longueur = strlen(buffer);
    if(buffer[longueur - 1] == '\n')
        buffer[longueur - 1] = '\0';

    return 1;
}



/**
 * \fn unsigned int get_prochain_unsigned_int(FILE* fichier, char buffer[4096])
 * \brief Fait un get_prochaine_ligne
 *        puis renvoie l'unsigned int qui, normalement, se trouve au
 *        debut de la ligne ainsi obtenue
 *
 * \param[in] fichier le fichier dans lequel on lit
 * \param buffer Un pointeur vers un tableau de 4096 caracteres
 * \return L'int lu. Si aucun entier n'a pu etre lu, 0 est renvoye.
 */
static unsigned int get_prochain_unsigned_int(FILE* fichier, char buffer[4096])
{
    if(get_prochaine_ligne(fichier, buffer) < 0)
        return 0;
    else
        return (unsigned int)atoi(buffer);
}



/**
 * \fn FILE* ouvrir_fichier(const char* nom_fichier)
 * \brief Ouvre un fichier en mode lecture uniquement ("r") et affiche
un message d'erreur avant de quitter l'application en cas d'erreur
 *
 * \param[in] nom_fichier Le chemin menant au fichier a ouvrir
 * \return L'adresse du fichier ouvert
 */
static FILE* ouvrir_fichier(const char* nom_fichier)
{
    FILE* fichier;

    if(!(fichier = fopen(nom_fichier, "r")))
    {
        printf("Erreur : fichier %s introuvable ou impossible a ouvrir.\n", nom_fichier);
        exit(EXIT_FAILURE);
    }
    else
        printf("Lecture de %s...\n", nom_fichier);

    return fichier;
}


/**
 * \fn void fermer_fichier(FILE* fichier)
 * \brief Ferme un fichier avec quelques printf conviviaux
 *
 * \param[in] fichier L'adresse du fichier a fermer
 */
static void fermer_fichier(FILE* fichier)
{
    fclose(fichier);
    printf("Termine\n");
}




/**
 * \fn void charger_liste_cases_constantes(struct carte_zone* carte, FILE* fichier,
                                           char buffer[4096])
 * \brief Remplit les champs liste_infos_cases_constantes et
 *        nb_types_cases_constantes d'une carte_zone en lisant un fichier
 *
 * \param[in,out] carte Pointeur vers la carte_zone concernee
 * \param[in] fichier Le fichier dans lequel on lit
 * \param buffer Un pointeur vers un tableau de 4096 caracteres
 */
static void charger_liste_cases_constantes(struct carte_zone* carte, FILE* fichier,
                                           char buffer[4096])
{
    int nb_types;
    int i;
    unsigned int id_tuile;
    char relief;
    struct case_constante nouveau_type;

    if(get_prochaine_ligne(fichier, buffer) < 0)
    {
        printf("Erreur 1 lors de la lecture de la liste des cases constantes\n");
        exit(EXIT_FAILURE);
    }
    nb_types = atoi(buffer);
    init_tab_dyn_cases_constantes(&carte->liste_infos_cases_constantes, nb_types);
    for(i = 0;  i < nb_types;  i++)
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("Erreur 2 lors de la lecture de la liste des cases constantes\n");
            exit(EXIT_FAILURE);
        }
        if(sscanf(buffer, "%u %c", &id_tuile, &relief) != 2)
        {
            printf("Erreur 3 lors de la lecture de la liste des cases constantes\n");
            exit(EXIT_FAILURE);
        }
        nouveau_type.id_tuile = id_tuile;
        switch(relief)
        {
            case 'p':
                nouveau_type.relief = RELIEF_PLAT;
                break;
            case 'h':
                nouveau_type.relief = RELIEF_POSITIF;
                break;
            case 'b':
                nouveau_type.relief = RELIEF_NEGATIF;
                break;
            default:
                printf("Erreur 4 lors de la lecture de la liste des cases constantes\n");
                exit(EXIT_FAILURE);
                break;
        }
        ajouter_case_constante(&carte->liste_infos_cases_constantes, &nouveau_type);
    }
}



/**
 * \fn void lire_rectangles_cases_constantes(struct carte_zone* carte, FILE* fichier, char buffer[4096])
 * \brief Lit les donnees qui permettent de placer les elements constants dans
 * une carte_zone a partir d'un fichier et les place dans la foulee
 *
 * \param[in,out] carte Un pointeur sur la carte_zone dont on veut s'occuper
 * \param[in] fichier Le fichier dans lequel on lit
 * \param buffer Un pointeur vers un tableau de 4096 caracteres
 */
static void lire_rectangles_cases_constantes(struct carte_zone* carte, FILE* fichier, char buffer[4096])
{
    unsigned int indice, x_min, y_min, x_max, y_max;

    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("Erreur 1 dans lire_rectangle_cases_constantes\n");
            exit(EXIT_FAILURE);
        }
        if(buffer[0] != 'q')
        {
            /*
             * ici, on a une ligne du type
             * <indice_de_type_de_case> <x_min> <y_min> <x_max> <y_max>
             */
            if(sscanf(buffer, "%u %u %u %u %u", &indice, &x_min, &y_min, &x_max, &y_max) != 5)
            {
                printf("Erreur 2 dans lire_rectangle_cases_constantes\n");
                exit(EXIT_FAILURE);
            }
            remplir_rectangle_cases_constantes(carte, indice, x_min, y_min, x_max, y_max);
        }
    } while(buffer[0] != 'q');
}



/**
 * \fn void lire_rectangles_cases_destructibles(struct carte_zone* carte, FILE* fichier, char buffer[4096]);
 * \brief Lit les donnees qui permettent de placer les elements destructibles dans
 * une carte_zone a partir d'un fichier et les place dans la foulee
 *
 * \param[in,out] carte Un pointeur sur la carte_zone dont on veut s'occuper
 * \param[in] fichier Le fichier dans lequel on lit
 * \param buffer Un pointeur vers un tableau de 4096 caracteres
 */
static void lire_rectangles_cases_destructibles(struct carte_zone* carte, FILE* fichier, char buffer[4096])
{
    unsigned int tuile, pts_vie, groupe, x_min, y_min, x_max, y_max;
    struct case_destructible donnees_case;

    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("Erreur 1 dans lire_rectangle_cases_destructibles\n");
            exit(EXIT_FAILURE);
        }
        if(buffer[0] != 'q')
        {
            /*
             * ici, on a une ligne du type
             * <id_tuile> <points_de_vie> <id_groupe> <x_min> <y_min> <x_max> <y_max>
             */
            if(sscanf(buffer, "%u %u %u %u %u %u %u",
                                  &tuile, &pts_vie, &groupe, &x_min,
                                  &y_min, &x_max, &y_max) != 7)
            {
                printf("Erreur 2 dans lire_rectangle_cases_destructibles\n");
                exit(EXIT_FAILURE);
            }
            donnees_case.groupe = groupe;
            donnees_case.id_tuile = tuile;
            donnees_case.pv_case = pts_vie;
            remplir_rectangle_cases_destructibles(carte, &donnees_case,
                                                  x_min, y_min, x_max, y_max);
        }
    } while(buffer[0] != 'q');
}




/**
 * \fn static unsigned char lire_infos_diverses_carte(struct carte_zone* carte, struct etat_bataille* bataille,
                                      FILE* fichier, char buffer[4096], const struct base_donnees* donnees)
 * \brief Lit les donnees diverses liees a une carte_zone dans le fichier des
 * cartes
 *
 * \param[out] carte Un pointeur vers la carte concernee
 * \param[out] bataille Un pointeur vers l'etat de la bataille qui va se derouler
 * \param[in] fichier Le fichier des cartes
 * \param buffer Un pointeur vers un tableau de 4096 caracteres
 * \param[in] donnees L'adresse d'une base_donnee correctement remplie.
 * \return Le nombre d'ennemis se trouvant dans la carte
 *
 * Pour avoir la liste des informations lues par cette procedure, voir les
 * commentaires de cartes.txt
 */
static unsigned char lire_infos_diverses_carte(struct carte_zone* carte, struct etat_bataille* bataille,
                               FILE* fichier, char buffer[4096], const struct base_donnees* donnees)
{
    unsigned int id, x, y;
    unsigned char nb_ennemis;

    nb_ennemis = 0;
    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("\"Infos diverses\" introuvables\n");
            exit(EXIT_FAILURE);
        }
        switch(buffer[0])
        {
            /* ajout d'un ennemi */
            case 'e':
                /*
                 * buffer contenant une ligne du type
                 * e <id_ennemi> <coord_x> <coord_y>
                 */
                sscanf(buffer + 2, "%u %u %u", &id, &x, &y);
                ajouter_ennemi(&bataille->ennemis, id, x, y, donnees);
                nb_ennemis++;
                break;
            /* modif de la somme d'argent */
            case 'a':
                carte->argent_zone = atoi(buffer + 2);
                break;
            /* modif du champ boss */
            case 'b':
                carte->boss = VRAI;
                break;
        }
    } while(buffer[0] != 'q');

    return nb_ennemis;
}




void charger_carte_zone(struct carte_zone* carte, struct etat_bataille* bataille,
                        const unsigned int num_region, const unsigned int num_zone,
                        const struct base_donnees* donnees, struct etat_partie* partie)
{
    FILE* fichier;
    char buffer[4096];
    unsigned int dim_x, dim_y;
    int i;
    unsigned int nb_objets_zone;
    struct ennemi* p_ennemi;
    unsigned char nb_ennemis;

    fichier = ouvrir_fichier("donnees/texte/cartes.txt");
    /*
     * premierement, on cherche a se placer sur le debut de la description
     * de la region dans laquelle se trouve la zone a charger :
     * une ligne qui doit donc ressembler a "$<num_region>"
     */
    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("charger_carte_zone : region %u introuvable\n", num_region);
            exit(EXIT_FAILURE);
        }
    } while(buffer[0] != '$' || atoi(buffer + 1) != num_region);
    /*
     * ensuite, on peut chercher la premiere occurence de
     * "&<num_zone>" au debut d'une ligne
     */
    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("charger_carte_zone : zone %u introuvable\n", num_zone);
            exit(EXIT_FAILURE);
        }
    } while(buffer[0] != '&' || atoi(buffer + 1) != num_zone);
    /*
     * REMARQUE : des commentaires au debut de cartes.txt expliquent
     *            en detail a quoi correspond ce qu'on trouve dans
     *            la description d'une zone
     * la localisation de la zone dans la region est ici inutile, car on
     * peut la trouver dans le descriptif de l'etat actuel de la partie.
     * On a ici dans le buffer une ligne du type :
     * &<numero_de_zone> <dim_x> <dim_y> <pos_depart_x> <pos_depart_y>...
     * ...<coord_x_dans_region> <coord_y_dans_region> <raccords>
     */
    sscanf(buffer + 1, "%u %u %u %u %u",
           &dim_x, &dim_x, &dim_y, &bataille->coord_j_x, &bataille->coord_j_y);
    init_carte_zone(carte, dim_x, dim_y);
    charger_liste_cases_constantes(carte, fichier, buffer);
    /*
     * on ne peut mettre le mur d'enceinte et le remplissage que si des types
     * de cases ont ete prevus a cet effet (rappel : ce sont
     * les 0 et 1 de la liste)
     */
    if(carte->liste_infos_cases_constantes.nb_elements > 1)
        mettre_contour_et_remplir(carte);
    lire_rectangles_cases_constantes(carte, fichier, buffer);
    lire_rectangles_cases_destructibles(carte, fichier, buffer);
    /* lecture des objets obtenus en cas de victoire */
    nb_objets_zone = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_id(&carte->tab_objets_zone, nb_objets_zone);
    for(i = 0;  i < nb_objets_zone;  i++)
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("charger_carte_zone : erreur dans la liste d'objets\n");
            exit(EXIT_FAILURE);
        }
        ajouter_id(&carte->tab_objets_zone, atoi(buffer));
    }
    /*
     * Les premieres actions de remplissage de l'etat_bataille se feront
     * dans la fonction lire_infos_diverses_carte, nous allons donc
     * l'initialiser maintenant
     */
    init_etat_bataille(bataille, partie, donnees);
    nb_ennemis = lire_infos_diverses_carte(carte, bataille, fichier, buffer, donnees);
    /*
     * il faut ensuite remplir la liste d'id d'ennemis de la carte, car, a ce
     * stade du chargement, nous avons la liste des ennemis dans
     * l'etat_bataille.
     */
    init_tab_dyn_id(&carte->liste_id_ennemis, nb_ennemis);
    p_ennemi = bataille->ennemis.prem;
    while(p_ennemi)
    {
        ajouter_id(&carte->liste_id_ennemis, p_ennemi->id);
        p_ennemi = p_ennemi->suiv;
    }
    carte->deja_visitee = voir_si_zone_est_terminee(partie, num_region, num_zone);

    fermer_fichier(fichier);
}




void lire_fichier_ennemis(struct base_donnees* donnees)
{
    int i, n;
    unsigned int id_objet, proba;
    FILE* fichier;
    char buffer[4096];
    struct description_ennemi descr;
    unsigned char nb_objets;
    unsigned int nb_ennemis_total;
    unsigned int recup_val1, recup_val2;

    fichier = ouvrir_fichier("donnees/texte/ennemis.txt");
    nb_ennemis_total = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_description_ennemis(&donnees->tab_ennemis, nb_ennemis_total);
    for(n = 0;  n < nb_ennemis_total;  n++)
    {
        /* remplissage des champs les plus basiques */
        get_prochaine_ligne(fichier, buffer);
        strcpy(descr.nom, buffer);
        descr.pv = get_prochain_unsigned_int(fichier, buffer);
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_ennemi : erreur 1\n");
            exit(EXIT_FAILURE);
        }
        if(3 != sscanf(buffer, "%u %u %u", &descr.vitesse_deplacement,
                                           &descr.puissance,
                                           &descr.vitesse_charge))
        {
            printf("lire_fichier_ennemi : erreur 2\n");
            exit(EXIT_FAILURE);
        }
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_ennemi : erreur 3\n");
            exit(EXIT_FAILURE);
        }
        if(3 != sscanf(buffer, "%u %u %u", &recup_val1,
                                           &recup_val2,
                                           &descr.localisation_sprites))
        {
            printf("lire_fichier_ennemi : erreur 4\n");
            exit(EXIT_FAILURE);
        }
        descr.id_projectile = (unsigned char)recup_val1;
        descr.taille = (unsigned char)recup_val2;
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_ennemi : erreur 5\n");
            exit(EXIT_FAILURE);
        }
        if(buffer[0] == 'c')
            descr.type_attaque = CONTACT;
        else
            descr.type_attaque = DISTANCE;
        if(buffer[2] == '0')
            descr.volant = FAUX;
        else
            descr.volant = VRAI;
        /*
         * remplissage de la liste de drops
         * (et des probas qui y correspondent)
         */
        nb_objets = (unsigned char)get_prochain_unsigned_int(fichier, buffer);
        init_tab_dyn_id(&descr.drops, nb_objets);
        init_tab_dyn_id(&descr.probas, nb_objets);
        for(i = 0;  i < nb_objets;  i++)
        {
            if(get_prochaine_ligne(fichier, buffer) < 0)
            {
                printf("lire_fichier_ennemi : erreur 6\n");
                exit(EXIT_FAILURE);
            }
            if(sscanf(buffer, "%u %u", &id_objet, &proba) != 2)
            {
                printf("lire_fichier_ennemi : erreur 7\n");
                exit(EXIT_FAILURE);
            }
            ajouter_id(&descr.drops, id_objet);
            ajouter_id(&descr.probas, proba);
        }
        ajouter_description_ennemi(&donnees->tab_ennemis, &descr);
    }
    fermer_fichier(fichier);
}



void lire_fichier_objets(struct base_donnees* donnees)
{
    int i, j;
    FILE* fichier;
    char buffer[4096];
    unsigned int nb_objets_total;
    struct description_objet nouvel_objet;
    unsigned int recup_val;

    fichier = ouvrir_fichier("donnees/texte/objets.txt");
    /* la premiere ligne indique le nombre d'objets repertories */
    nb_objets_total = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_description_objets(&donnees->tab_objets, nb_objets_total);
    for(i = 0;  i < nb_objets_total;  i++)
    {
        /*
         * A chaque fois, on a un bloc de deux lignes de la forme :
         * <nom> [31 caracteres maxi, espaces autorises]
         * <type> <pv> <vit_deplac> <puiss> <vit_charge> <id_proj> <prix_vente>
         */
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_objet : erreur 1\n");
            exit(EXIT_FAILURE);
        }
        for(j = 0;  j < 31;  j++)
            nouvel_objet.nom[j] = buffer[j];
        nouvel_objet.nom[31] = '\0';
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_objet : erreur 2\n");
            exit(EXIT_FAILURE);
        }
        if(sscanf(buffer, "%c %u %u %u %u %u %u", &nouvel_objet.type,
                      &nouvel_objet.pv, &nouvel_objet.vitesse_deplacement,
                      &nouvel_objet.puissance, &nouvel_objet.vitesse_charge,
                      &recup_val, &nouvel_objet.prix_vente) != 7)
        {
            printf("lire_fichier_objet : erreur 3\n");
            exit(EXIT_FAILURE);
        }
        nouvel_objet.id_projectile = (unsigned char)recup_val;
        ajouter_description_objet(&donnees->tab_objets, &nouvel_objet);
    }
    fermer_fichier(fichier);
}



void lire_fichier_projectiles(struct base_donnees* donnees)
{
    int i;
    FILE* fichier;
    unsigned int nb_proj_total, recup_val;
    char buffer[4096];
    struct description_projectile nouveau_projectile;

    fichier = ouvrir_fichier("donnees/texte/projectiles.txt");
    /* la premiere ligne indique le nombre de projectiles repertories */
    nb_proj_total = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_description_projectiles(&donnees->tab_projectiles, nb_proj_total);
    for(i = 0;  i < nb_proj_total;  i++)
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_projectiles : erreur 1\n");
            exit(EXIT_FAILURE);
        }
        if(sscanf(buffer, "%u %u %u",
                      &nouveau_projectile.localisation_sprites,
                      &nouveau_projectile.vitesse_deplacement,
                      &recup_val) != 3)
        {
            printf("lire_fichier_projectiles : erreur 2\n");
            exit(EXIT_FAILURE);
        }
        nouveau_projectile.taille = (unsigned char)recup_val;
        ajouter_description_projectile(&donnees->tab_projectiles, &nouveau_projectile);
    }
    fermer_fichier(fichier);
}



void lire_fichier_niveaux(struct base_donnees* donnees)
{
    int i;
    FILE* fichier;
    char buffer[4096];

    fichier = ouvrir_fichier("donnees/texte/niveaux.txt");
    for(i = 0;  i < 98;  i++)
    {
        /* un palier nul signifierait que la lecture a echouee ! */
        if(0 == (donnees->paliers_xp[i] = get_prochain_unsigned_int(fichier, buffer)))
        {
            printf("Erreur : un palier d'xp nul a ete lu\n");
            exit(EXIT_FAILURE);
        }
    }
    fermer_fichier(fichier);
}



void lire_fichier_dependances(struct base_donnees* donnees)
{
    int i, j;
    FILE* fichier;
    char buffer[4096];
    unsigned int nb_regions_a_traiter;
    unsigned int nb_dependances;
    unsigned int id_region_concernee;

    fichier = ouvrir_fichier("donnees/texte/dependances.txt");
    nb_regions_a_traiter = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dependances(&donnees->dependances, nb_regions_a_traiter);
    for(i = 0;  i < nb_regions_a_traiter;  i++)
    {
        id_region_concernee = get_prochain_unsigned_int(fichier, buffer);
        nb_dependances = get_prochain_unsigned_int(fichier, buffer);
        init_dependance(&donnees->dependances, id_region_concernee, nb_dependances);
        for(j = 0;  j < nb_dependances;  j++)
            ajouter_dependance(&donnees->dependances, get_prochain_unsigned_int(fichier, buffer));
    }
    fermer_fichier(fichier);
}



void lire_fichier_pour_exploration(struct etat_exploration* exploration,
                                   const unsigned int id_region,
                                   const struct base_donnees* donnees)
{
    int i;
    FILE* fichier;
    char buffer[4096];
    /* raccord est [5] et non [4] car sscanf rajoute '\0' */
    char raccords[5];
    int num_zone, coord_x, coord_y;
    int valeur_inutile;
    struct zone* p_zone;

    fichier = ouvrir_fichier("donnees/texte/cartes.txt");
    /* on cherche tout d'abord la region concernee */
    do
    {
        if(get_prochaine_ligne(fichier, buffer) < 0)
        {
            printf("lire_fichier_pour_exploration : region %u introuvable\n", id_region);
            exit(EXIT_FAILURE);
        }
    } while(buffer[0] != '$' || atoi(buffer + 1) != id_region);
    /*
     * on extrait le nom de la base de donnees
     * (juste un pointeur ! On ne recopie pas la chaine !)
     */
    exploration->nom_region = get_nom_region(donnees, id_region);
    /* puis on lit le fichier jusqu'a arriver a la fin ou a une autre region */
    while((get_prochaine_ligne(fichier, buffer) > 0) && buffer[0] != '$')
    {
        /* si on trouve une nouvelle zone... */
        if(buffer[0] == '&')
        {
            /*
             * le buffer contient alors une ligne du type :
             * &<numero_de_zone> <dim_x> <dim_y> <pos_depart_x> <pos_depart_y>...
             * ...<coord_x_dans_region> <coord_y_dans_region> <raccords>
             */
            /* le + 1 permet de lire juste apres le & */
            if(8 != sscanf(buffer + 1, "%d %d %d %d %d %d %d %4s",
                           &num_zone, &valeur_inutile, &valeur_inutile,
                           &valeur_inutile, &valeur_inutile,
                           &coord_x, &coord_y, raccords))
            {
                printf("Erreur 2 dans lire_fichier_pour_exploration\n");
                exit(EXIT_FAILURE);
            }
            if(!(p_zone = malloc(sizeof(struct zone))))
                exit(EXIT_MALLOC);
            exploration->tab_zones[coord_x + 9 * coord_y] = p_zone;
            p_zone->exploree = FAUX;
            p_zone->id_zone = num_zone;
            for(i = 0;  i < 4;  i++)
            {
                if(raccords[i] != '-')
                    p_zone->raccords[i] = VRAI;
                else
                    p_zone->raccords[i] = FAUX;
            }
        }
    }
    fermer_fichier(fichier);
}



void lire_fichier_magasin(struct base_donnees* donnees)
{
    FILE* fichier;
    char buffer[4096];
    unsigned int nb_elements;
    int i;

    fichier = ouvrir_fichier("donnees/texte/magasin.txt");

    nb_elements = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_id(&donnees->objets_en_vente, nb_elements);
    for(i = 0;  i < nb_elements;  i++)
        ajouter_id(&donnees->objets_en_vente,
                   get_prochain_unsigned_int(fichier, buffer));

    fermer_fichier(fichier);
}



void lire_fichier_quetes(struct base_donnees* donnees)
{
    FILE* fichier;
    char buffer[4096];
    unsigned int nb_elements;
    int i;
    unsigned int id_objet, quantite;

    fichier = ouvrir_fichier("donnees/texte/quetes.txt");

    nb_elements = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_quetes(&donnees->tab_quetes, nb_elements);
    for(i = 0;  i < nb_elements;  i++)
    {
        init_prochaine_quete(&donnees->tab_quetes,
                             get_prochain_unsigned_int(fichier, buffer));
        while(get_prochaine_ligne(fichier, buffer) > 0
              && buffer[0] != 'q')
        {
            if(sscanf(buffer, "%u %u", &id_objet, &quantite) != 2)
            {
                printf("Erreur 1 dans lire_fichier_quetes\n");
                exit(EXIT_FAILURE);
            }
            modifier_derniere_quete(&donnees->tab_quetes,
                                    id_objet, quantite);
        }
    }

    fermer_fichier(fichier);
}



void lire_fichier_cartes(struct base_donnees* donnees)
{
    FILE* fichier;
    char buffer[4096];
    unsigned int nb_elements;
    int i, j;
    char nom_region[32];
    unsigned int id_region;
    unsigned int coord_x, coord_y;

    fichier = ouvrir_fichier("donnees/texte/cartes.txt");

    nb_elements = get_prochain_unsigned_int(fichier, buffer);
    init_tab_dyn_regions(&donnees->tab_regions, nb_elements);
    for(i = 0;  i < nb_elements;  i++)
    {
        do
        {
            if(get_prochaine_ligne(fichier, buffer) < 0)
            {
                printf("Nombre de regions incorrect dans cartes.txt\n");
                exit(EXIT_FAILURE);
            }
        } while(buffer[0] != '$');
        /*
         * ici, le buffer contient une ligne du type :
         * $<numero_de_region> <nom_de_region> <coord_x> <coord_y>
         */
        /* le + 1 permet de lire juste apres le $ */
        if(4 != sscanf(buffer + 1, "%u %31s %u %u",
                       &id_region, nom_region, &coord_x, &coord_y))
        {
            printf("Erreur 1 dans lire_fichier_cartes\n");
            exit(EXIT_FAILURE);
        }
        /*
         * le nom de region contient des '_' au lieu
         * des ' '. Il faut proceder a un remplacement
         */
        j = 0;
        while(nom_region[j])
        {
            if(nom_region[j] == '_')
                nom_region[j] = ' ';
            j++;
        }
        ajouter_region(&donnees->tab_regions, id_region,
                       nom_region, coord_x, coord_y);
    }

    fermer_fichier(fichier);
}



FILE* creer_fichier_sauvegarde(const char* nom_fichier)
{
    char buffer[128];
    FILE* fichier;

    sprintf(buffer, "donnees/parties/%s", nom_fichier);
    if((fichier = fopen(buffer, "wb")) == NULL)
    {
        printf("Erreur lors de l'ouverture de %s\n", buffer);
        exit(EXIT_FAILURE);
    }
    return fichier;
}



void remplir_fichier_sauvegarde(const struct etat_partie* partie, FILE* fichier)
{
    unsigned int t_u_char, t_char, t_u_int, t_int;
    int i;
    unsigned int nb_elem;
    unsigned char nb_zones;
    struct objet* p_objet;

    GET_TAILLES(t_u_char, t_char, t_u_int, t_int);
    /*
     * ordre d'ecriture des donnees :
     *
     * experience
     * argent
     * statistiques
     * region
     * zone
     * arme
     * veste
     * gants
     * chaussures
     * accessoire
     * objets_utilisables [3]
     *
     * nb de regions dans zones terminees
     *      id premiere region
     *      nb de zones dans premiere region de zones terminees (unsigned char)
     *          id de zone
     *          id de zone
     *          ...
     *      id deuxieme region
     *      nb de zones dans deuxieme region de zones terminees (unsigned char)
     *          id de zone
     *          id de zone
     *          ...
     *      ...
     *
     * nb de regions reussies (unsigned int)
     *      id de region
     *      id de region
     *      ...
     *
     * objets_possedes : id puis quantite
     * EOF
     */
    /*
     * Note : le nom n'est pas ecrit dans le fichier. Cela
     * serait redondant car c'est le nom du fichier
     */
    fwrite(&partie->experience, t_u_int, 1, fichier);
    fwrite(&partie->argent, t_u_int, 1, fichier);
    fwrite(&partie->statistiques, sizeof(struct stats), 1, fichier);
    fwrite(&partie->region, t_u_int, 1, fichier);
    fwrite(&partie->zone, t_u_char, 1, fichier);
    fwrite(&partie->arme, t_u_int, 1, fichier);
    fwrite(&partie->veste, t_int, 1, fichier);
    fwrite(&partie->gants, t_int, 1, fichier);
    fwrite(&partie->chaussures, t_int, 1, fichier);
    fwrite(&partie->accessoire, t_int, 1, fichier);
    fwrite(partie->objets_utilisables, t_int, 3, fichier);
    /* zones terminees */
    nb_elem = partie->terminees.nb_elem;
    fwrite(&nb_elem, t_u_int, 1, fichier);
    for(i = 0;  i < nb_elem;  i++)
    {
        fwrite(&partie->terminees.tab[i].id_region, t_u_int, 1, fichier);
        nb_zones = partie->terminees.tab[i].nb_elem;
        fwrite(&nb_zones, t_u_char, 1, fichier);
        fwrite(partie->terminees.tab[i].tab, t_u_char, nb_zones, fichier);
    }
    /* regions reussies */
    nb_elem = partie->reussies.regions.nb_elements;
    fwrite(&nb_elem, t_u_int, 1, fichier);
    fwrite(partie->reussies.regions.tab, t_u_int, nb_elem, fichier);
    /* inventaire */
    p_objet = partie->objets_possedes.prem;
    while(p_objet)
    {
        fwrite(&p_objet->id, t_u_int, 1, fichier);
        fwrite(&p_objet->quantite, t_u_int, 1, fichier);
        p_objet = p_objet->suiv;
    }
}



void fermer_sauvegarde(FILE* fichier)
{
    fclose(fichier);
}



FILE* ouvrir_fichier_sauvegarde(const char* nom_partie)
{
    char buffer[128];
    FILE* fichier;

    sprintf(buffer, "donnees/parties/%s", nom_partie);
    if((fichier = fopen(buffer, "rb")) == NULL)
    {
        printf("Erreur lors de l'ouverture de %s\n", buffer);
        exit(EXIT_FAILURE);
    }
    return fichier;
}



void lire_fichier_partie(FILE* fichier, struct etat_partie* partie)
{
    unsigned int t_u_char, t_char, t_u_int, t_int;
    int i, j;
    unsigned int nb_elem;
    unsigned char nb_zones;
    unsigned int id_region;
    unsigned char id_zone;
    unsigned int id_objet;
    unsigned int quantite;
    int controle;

    GET_TAILLES(t_u_char, t_char, t_u_int, t_int);
    /*
     * ordre des donnees dans le fichier :
     *
     * experience
     * argent
     * statistiques
     * region
     * zone
     * arme
     * veste
     * gants
     * chaussures
     * accessoire
     * objets_utilisables [3]
     *
     * nb de regions dans zones terminees
     *      nb de zones dans premiere region de zones terminees (unsigned char)
     *          id de zone
     *          id de zone
     *          ...
     *      nb de zones dans deuxieme region de zones terminees (unsigned char)
     *          id de zone
     *          id de zone
     *          ...
     *      ...
     *
     * nb de regions reussies (unsigned int)
     *      id de region
     *      id de region
     *      ...
     *
     * objets_possedes : id puis quantite
     * EOF
     */
    fread(&partie->experience, t_u_int, 1, fichier);
    fread(&partie->argent, t_u_int, 1, fichier);
    fread(&partie->statistiques, sizeof(struct stats), 1, fichier);
    fread(&partie->region, t_u_int, 1, fichier);
    fread(&partie->zone, t_u_char, 1, fichier);
    fread(&partie->arme, t_u_int, 1, fichier);
    fread(&partie->veste, t_int, 1, fichier);
    fread(&partie->gants, t_int, 1, fichier);
    fread(&partie->chaussures, t_int, 1, fichier);
    fread(&partie->accessoire, t_int, 1, fichier);
    fread(partie->objets_utilisables, t_int, 3, fichier);
    /* zones terminees */
    fread(&nb_elem, t_u_int, 1, fichier);
    init_zones_terminees(&partie->terminees, nb_elem);
    for(i = 0;  i < nb_elem;  i++)
    {
        fread(&id_region, t_u_int, 1, fichier);
        fread(&nb_zones, t_u_char, 1, fichier);
        for(j = 0;  j < nb_zones;  j++)
        {
            fread(&id_zone, t_u_char, 1, fichier);
            marquer_zone_terminee(&partie->terminees, id_region, id_zone);
        }
    }
    /* regions reussies */
    fread(&nb_elem, t_u_int, 1, fichier);
    init_regions_terminees(&partie->reussies, nb_elem);
    for(i = 0;  i < nb_elem;  i++)
    {
        fread(&id_region, t_u_int, 1, fichier);
        marquer_region_terminee(&partie->reussies, id_region);
    }
    /* inventaire */
    do
    {
        /*
         * puisque l'inventaire est en fin de fichier, on lit jusqu'a ce
         * que la quantite de donnees lues ne soit pas celle attendue
         */
        controle = fread(&id_objet, t_u_int, 1, fichier);
        controle += fread(&quantite, t_u_int, 1, fichier);
        if(controle == 2)
            ajouter_exemplaires(&partie->objets_possedes, id_objet, quantite);
    } while(controle == 2);
}



void charger_partie(struct etat_partie* partie, const char* nom_partie,
                    const char nouvelle_partie)
{
    FILE* fichier;

    init_inventaire(&partie->objets_possedes);

    if(nouvelle_partie)
    {
        /* creation d'une nouvelle partie */
        init_partie(partie);
        strcpy(partie->nom, nom_partie);
    }
    else
    {
        /* chargement de partie existante */
        strcpy(partie->nom, nom_partie);
        fichier = ouvrir_fichier_sauvegarde(nom_partie);
        lire_fichier_partie(fichier, partie);
        fermer_sauvegarde(fichier);
    }
}
